package cn.ztuo.bitrade.service;

import cn.ztuo.bitrade.constant.BooleanEnum;
import cn.ztuo.bitrade.constant.SysConstant;
import cn.ztuo.bitrade.core.DB;
import cn.ztuo.bitrade.dao.ContractPositionMapper;
import cn.ztuo.bitrade.entity.*;
import cn.ztuo.bitrade.pagination.Criteria;
import cn.ztuo.bitrade.pagination.Restrictions;
import cn.ztuo.bitrade.util.ContractUtils;
import cn.ztuo.bitrade.util.MessageResult;
import cn.ztuo.bitrade.util.RedisUtil;
import cn.ztuo.bitrade.vo.position.ContractPositionFindReqVo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class ContractPositionService {

    private Logger logger = LoggerFactory.getLogger(ContractPositionService.class);

    @Autowired
    private ContractPositionMapper contractPositionMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ContractOrderService contractOrderService;


    public Page<ContractPosition> findAllByPage(ContractPositionFindReqVo reqVo) {
        Sort orders = new Sort(new Sort.Order(Sort.Direction.DESC, "createTime"));
        if(reqVo.getPageNo()==null||reqVo.getPageNo()==0){
            reqVo.setPageNo(1);
        }
        if(reqVo.getPageSize()==null||reqVo.getPageSize()==0){
            reqVo.setPageSize(20);
        }
        PageRequest pageRequest = new PageRequest(reqVo.getPageNo() - 1, reqVo.getPageSize(), orders);
        Criteria<ContractPosition> specification = new Criteria<ContractPosition>();
        if(reqVo.getMemberId()!=null){
            specification.add(Restrictions.eq("memberId", reqVo.getMemberId(), true));
        }
        if(reqVo.getPositionType()!=null){
                specification.add(Restrictions.eq("positionType", reqVo.getPositionType(), true));
        }
        if(StringUtils.isNotBlank(reqVo.getSymbol())){
            specification.add(Restrictions.eq("symbol", reqVo.getSymbol(), true));
        }
        specification.add(Restrictions.gt("holdingBond", 0, true));
        specification.add(Restrictions.gt("contractTotalNum", 0, true));
        specification.add(Restrictions.ne("memberId", 0, true));
        specification.add(Restrictions.ne("memberId", 1, true));
        specification.add(Restrictions.ne("positionAvgPrice",null,false));
        return contractPositionMapper.findAll(specification, pageRequest);
    }



    public List<ContractPosition> findPositions(ContractPosition position) {
        Example<ContractPosition> example = Example.of(position);
        List<ContractPosition> list = contractPositionMapper.findAll(example);
        list = list.stream().filter(a -> a.getContractTotalNum() > 0).map(a -> {
            ContractCoin coin = ContractUtils.getBaseSymbol(a.getSymbol());
            a.setBaseCoin(coin.getCoinSymbol());
            return a;
        }).collect(Collectors.toList());
        return list;
    }

    public List<ContractPosition> findByMemberIdAndSymbol(Long memberId, String symbol) {
        return contractPositionMapper.findByMemberIdAndSymbol(memberId, symbol);
    }

    public List<ContractPosition> findAll() {
        return contractPositionMapper.findAllByStatusAndIsLock(0, BooleanEnum.IS_FALSE);
    }

    public ContractPosition getByPositionTypeAndSymbolAndMemberId(ContractOrderPositionType positionType, String symbol, Long memberId) {
        return contractPositionMapper.getByPositionTypeAndSymbolAndMemberId(positionType, symbol, memberId);
    }

    @Transactional(rollbackFor = Exception.class)
    public ContractPosition saveAndFlush(ContractPosition position) {
        position = contractPositionMapper.saveAndFlush(position);
        if (position != null && position.getId() != null) {
            redisUtil.hset(SysConstant.CONTRACT_POSITION, position.getId().toString(), position);
        }
        return position;
    }

    @Transactional(rollbackFor = Exception.class)
    public MessageResult updatePositionByDesignate(ContractPosition position,
                                                   BigDecimal amount,
                                                   Integer tradeNowp,
                                                   BigDecimal avgPrice,
                                                   BigDecimal forcePrice,
                                                   String currentMultiple,
                                                   BigDecimal bond) {
        int ret = contractPositionMapper.updatePositionByDesignate(position.getId(),
                amount,
                tradeNowp,
                avgPrice,
                forcePrice,
                currentMultiple,
                bond);
        if (ret > 0) {
            return MessageResult.success();
        } else {
            return MessageResult.error("Information Expired");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public MessageResult updatePositionByThrowout(ContractPosition position,
                                                  BigDecimal amount,
                                                  Integer tradeNowp,
                                                  BigDecimal bond,
                                                  BigDecimal avgPrice) {
        int ret = contractPositionMapper.updatePositionByThrowout(position.getId(),
                amount,
                tradeNowp,
                bond,
                avgPrice);
        if (ret > 0) {
            return MessageResult.success();
        } else {
            return MessageResult.error("Information Expired");
        }
    }

    public void delPosition(ContractPosition position) {
        contractPositionMapper.delete(position);
        redisUtil.hdel(SysConstant.CONTRACT_POSITION, position.getId().toString());
    }

    @Transactional(rollbackFor = Exception.class)
    public MessageResult thawBalance(ContractPosition position, int intValue, BigDecimal bond) {
        int ret = contractPositionMapper.thawBalance(position.getId(), intValue, bond);
        if (ret > 0) {
            return MessageResult.success();
        } else {
            return MessageResult.error("Information Expired");
        }
    }

    @Transactional
    public MessageResult forcePosition(List<ContractOrder> needCancelOrders, ContractPosition a, BigDecimal dealPrice, BigDecimal gainLossRate, ContractCoin coin) {
        MessageResult mr = null;
        try {
            DB.query("select id from contract_wallet where member_id = ? for update;", a.getMemberId());
            DB.query("select id from contract_position where member_id = ? and contract_position_type = ? for update;", a.getMemberId(), a.getPositionType());
            BigDecimal cancelStart = BigDecimal.ZERO;
            for(ContractOrder order : needCancelOrders){
                contractOrderService.orderForceCanceled(order.getOrderId(), order.getStart(), order.getTradedAmount(), order.getTurnover());
                cancelStart= cancelStart.add(order.getStart());
            }
            ContractPosition position = getByPositionTypeAndSymbolAndMemberId(a.getPositionType(), a.getSymbol(), a.getMemberId());
            position.setUseableBond(position.getUseableBond().add(cancelStart));
            ContractOrder order = new ContractOrder();
            if (gainLossRate.compareTo(new BigDecimal(-0.8)) <= 0) {
                order.setStopType(2);
            }
            if (gainLossRate.compareTo(new BigDecimal(1)) >= 0) {
                order.setStopType(1);
            }

            if (position.getPositionType() == ContractOrderPositionType.RISE) {
                order.setDirection(ContractOrderDirection.SELL);
            }
            if (position.getPositionType() == ContractOrderPositionType.FALL) {
                order.setDirection(ContractOrderDirection.BUY);
            }
//                    order.setForcePrice(dealPrice);
            order.setPrice(dealPrice);
            order.setPositionFlag(ContractEnumType.ContractOrderPositionFlag.throwout);
            order.setPositionOprationType(ContractEnumType.ContractOrderPositionOprationType.force);
            order.setPositionType(position.getPositionType());
            order.setNowp(new BigDecimal(position.getContractTotalNum()));
            order.setSymbol(position.getSymbol());
            order.setStatus(ContractOrderStatus.NODEAL);
            order.setBaseSymbol(coin.getBaseSymbol());
            order.setCoinSymbol(coin.getCoinSymbol());
            order.setListTime(Calendar.getInstance().getTimeInMillis());
            order.setAmount(new BigDecimal(position.getContractTotalNum()).multiply(coin.getMinVolume()));
            order.setMemberId(position.getMemberId());
            order.setOrderResource(ContractOrderResource.API);
            order.setType(ContractOrderType.MARKET_PRICE);
            order.setPositionValue(order.getAmount().multiply(dealPrice));
            order.setMultiple(new BigDecimal(a.getCurrentMultiple()));

            order.setStart(position.getHoldingBond());
            BigDecimal fee = ContractUtils.getFee(order.getMemberId(), coin, order.getNowp().multiply(dealPrice).multiply(coin.getMinVolume()));
            order.setFee(fee);

            mr = contractOrderService.addForceOrder(order.getMemberId(), order,position);
            logger.info("添加挂单成功=====================================================");
            if (mr.getCode() != 0) {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            }
            return mr;
        } catch (Exception e) {
            logger.error("强平异常------------ e={}", e);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return mr;
    }

    public List<ContractPosition> findByMemberId(long memberId) {
        List<ContractPosition> list = contractPositionMapper.findByMemberId(memberId);
        return list;
    }

    public ContractPosition getById(Long id) {
        return contractPositionMapper.getById(id);
    }
}
